#pragma once
// 线程池类实现
#include <iostream>
#include <vector>
#include <queue>
#include <unistd.h>
const static int defaultNum = 5;
struct ThreadData
{
    pthread_t tid_;
    std::string threadname_;
};
template <class T>
class threadpool
{
public:
    void lock()
    {
        pthread_mutex_lock(&mutex_);
    }
    void unlock()
    {
        pthread_mutex_unlock(&mutex_);
    }
    void wakeup() // 唤醒
    {
        pthread_cond_signal(&cond_);
    }
    void sleep_t() // 休眠（到条件变量的等待队列里面等）
    {
        pthread_cond_wait(&cond_, &mutex_);
    }
    bool isempty()
    {
        return task_queue.empty();
    }
    std::string getThreadname(pthread_t tid)
    {
        for (auto &e : threads_)
        {
            if (e.tid_ == tid)
                return e.threadname_;
        }
        return "None";
    }

public:
    void push(const T &task)
    {
        lock();
        task_queue.push(task);
        wakeup();
        unlock();
    }
    static void *HandlerTask(void *args) // 类内成员函数有this指针，会参数不匹配，加static修饰就没有了
    {
        threadpool<T> *tp = static_cast<threadpool<T> *>(args);
        std::string name=tp->getThreadname(pthread_self());
        while (true)
        {
            tp->lock();
            // 1.获取任务
            while (tp->isempty()) // 防止伪唤醒
            {
                tp->sleep_t();
            }
            T t =tp->pop();
            tp->unlock();
            // 2.消费任务
            t();
            cout<<name<<" : consum a task,taskresult is:"<<t.getResult()<<endl;
        }
    }
    void start()
    {
        int threadcout = threads_.size();
        for (int i = 0; i < threadcout; i++)
        {

            // 创建的同时把线程数组里的数据初始化好
            threads_[i].threadname_ = "thread-" + std::to_string(i + 1);
            pthread_create(&(threads_[i].tid_), nullptr, HandlerTask, this); // static成员函数不能访问成员变量，只能通过类对象访问
        }
    }

    T pop()
    {
        T out = task_queue.front();
        task_queue.pop();
        return out;
    }
    static threadpool<T> *GetInstacne() // 获取实例
    {                                   // 如果有多个线程同时进来获取实例呢？如果不上锁会导致对象被实例化多份出来
        if (tp_ == nullptr)             // 后面进来的大部分线程都会判断失败，不需要继续往获取锁
        {
            // 走到这里的只有一小部分线程
            pthread_mutex_lock(&lock_); // 上锁
            if (tp_ == nullptr)         // 只有第一次获取单例的线程需要进行条件判断，后续线程进来判断都失败
            {
                tp_ = new threadpool<T>();
            }
            pthread_mutex_unlock(&lock_); // 解锁
        }
        return tp_;
    }

private:
    std::vector<ThreadData> threads_; // 存放线程信息（通过里面的tid找到线程）
    queue<T> task_queue;              // 存放任务的队列
    pthread_mutex_t mutex_;           // 访问队列的时候需要上锁
    pthread_cond_t cond_;             // 如果没有任务就去里面等
    threadpool(const T &) = delete;   // 把一切可构造出第二个对象的成员函数禁掉
    const threadpool<T> &operator=(const threadpool<T> &) = delete;
    threadpool(int threadnum = defaultNum)
        : threads_(threadnum)
    {
        // 初始化锁跟条件变量
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&cond_, nullptr);
    }
    ~threadpool()
    {
        // 销毁锁和条件变量
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }
    static threadpool<T> *tp_;
    static pthread_mutex_t lock_;
};

template <class T> // 静态类成员类外定义
threadpool<T> *threadpool<T>::tp_ = nullptr;

template <class T>
pthread_mutex_t threadpool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER; // 如果用全局锁需要对锁进行初始化跟销毁